package main

import (
	"context"
	"fmt"
	"github.com/go-netty/go-netty"
	"github.com/go-netty/go-netty/codec/format"
	"github.com/go-netty/go-netty/codec/frame"
	"log"
	"runtime/debug"
	"sync"
	"time"
)

func main() {

	//完美的解决了 断线重连，心跳包
	//重发也不是问题了，回执用sync.map()解决
	go buildConnect(context.Background())
	select {}
}

func buildConnect(ctx context.Context) {
	closeFlag := make(chan struct{})
	defer close(closeFlag)
	for {
		clientInitializer := func(channel netty.Channel) {
			channel.Pipeline().
				//AddLast(frame.DelimiterCodec(1024, "\n", true), format.TextCodec(), netty.WriteIdleHandler(time.Second),&HeartBeatEventHandler{idleTime: time.Second}, &ClientHandler{closeFlag: closeFlag})
				AddLast(frame.DelimiterCodec(1024, "\n", true), format.TextCodec(), &HeartBeatEventHandler{idleTime: time.Second}, &ClientHandler{closeFlag: closeFlag})
		}
		bs := netty.NewBootstrap(netty.WithClientInitializer(clientInitializer))
		if channel, err := bs.Connect("127.0.0.1:5999", nil); err != nil {
			log.Printf("%s", err)
		} else {
			channel.Write("connect")
			//
			//t := time.NewTicker(time.Second)

			select {
			case <-ctx.Done():
				//t.Stop()
				if channel.IsActive() {
					if err = channel.Close(); err != nil {
						log.Printf("%s", err)
					}
				}
				return
			//case <-t.C:
			//	channel.Pipeline().FireChannelEvent(HeartBeat{})
			case <-closeFlag:
				//channel.Pipeline().
				time.Sleep(time.Second)
				//t.Stop()
				if err = channel.Close(); err != nil {
					log.Printf("%s", err)
				}

			}
		}

	}

}

//type ChildHandler struct {
//	LastReceivedTime time.Time
//}
//
//func (c *ChildHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
//	fmt.Println("Server.HandleRead: ", message)
//	c.LastReceivedTime = time.Now()
//	ctx.HandleRead(message)
//}
//
//func (c *ChildHandler) HandleEvent(ctx netty.EventContext, event netty.Event) {
//
//	switch event.(type) {
//	case netty.ReadIdleEvent:
//		if time.Since(c.LastReceivedTime) > time.Second * 3 {
//			fmt.Println("Server.HandleEvent: check heartbeat timeout... close the server connection")
//			ctx.Close(fmt.Errorf("check heartbeat failed"))
//		}
//	default:
//		ctx.HandleEvent(event)
//	}
//}

type HeartBeatEvent struct{}

// HeartBeatEventHandler 心跳Handler
type HeartBeatEventHandler struct {
	mutex         sync.RWMutex
	idleTime      time.Duration
	lastWriteTime time.Time
	writeTimer    *time.Timer
	handlerCtx    netty.HandlerContext
}

func (w *HeartBeatEventHandler) withLock(fn func()) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	fn()
}

func (w *HeartBeatEventHandler) withReadLock(fn func()) {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	fn()
}

func (w *HeartBeatEventHandler) HandleActive(ctx netty.ActiveContext) {

	// cache context
	w.withLock(func() {
		w.handlerCtx = ctx
		w.lastWriteTime = time.Now()
		w.writeTimer = time.AfterFunc(w.idleTime, w.onWriteTimeout)
	})

	// post the active event.
	ctx.HandleActive()
}

func (w *HeartBeatEventHandler) HandleWrite(ctx netty.OutboundContext, message netty.Message) {

	// update last write time.
	w.withLock(func() {
		w.lastWriteTime = time.Now()
		// reset timer.
		if w.writeTimer != nil {
			w.writeTimer.Reset(w.idleTime)
		}
	})

	// post write event.
	ctx.HandleWrite(message)
}

func (w *HeartBeatEventHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {

	w.withLock(func() {
		// reset context
		w.handlerCtx = nil
		// stop the timer.
		if w.writeTimer != nil {
			w.writeTimer.Stop()
			w.writeTimer = nil
		}
	})

	// post the inactive event.
	ctx.HandleInactive(ex)
}

func (w *HeartBeatEventHandler) onWriteTimeout() {

	var expired bool
	var ctx netty.HandlerContext

	w.withReadLock(func() {
		// check if the idle time expires.
		expired = time.Since(w.lastWriteTime) >= w.idleTime
		ctx = w.handlerCtx
	})

	// check if the idle time expires
	if expired && ctx != nil {
		// trigger event.
		func() {
			// capture exception
			defer func() {
				if err := recover(); nil != err {
					ctx.Channel().Pipeline().FireChannelException(netty.AsException(err, debug.Stack()))
				}
			}()

			// trigger WriteIdleEvent.
			ctx.Trigger(HeartBeatEvent{})
		}()
	}

	// reset timer.
	w.withReadLock(func() {
		if w.writeTimer != nil {
			w.writeTimer.Reset(w.idleTime)
		}
	})

}



type ClientHandler struct {
	SendHeartbeatNum int
	closeFlag        chan struct{}
}

func (c *ClientHandler) HandleActive(ctx netty.ActiveContext) {
	fmt.Println("Client.HandleActive: ", ctx.Channel().RemoteAddr())
	ctx.HandleActive()
}

func (c *ClientHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
	fmt.Println("Client.HandleInactive: ", ctx.Channel().RemoteAddr(), " ", ex)
	ctx.HandleInactive(ex)
	c.closeFlag <- struct{}{}

}

//func (c *ClientHandler) HandleWrite(ctx netty.OutboundContext, message netty.Message) {
//	//panic("implement me")
//	ctx.HandleWrite(message)
//}

func (c *ClientHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
	fmt.Println("Client.HandleRead: ", message)
	ctx.HandleRead(message)

}

func (c *ClientHandler) HandleEvent(ctx netty.EventContext, event netty.Event) {

	switch event.(type) {

	case netty.WriteIdleEvent:

	case HeartBeatEvent:
		//if c.SendHeartbeatNum < 5 {
		fmt.Println("Client.HandleEvent: ", "sending heartbeat message... ", c.SendHeartbeatNum)
		ctx.Write("Heartbeat")
		c.SendHeartbeatNum++
		//}
	default:
		ctx.HandleEvent(event)
	}
}
